#include "stdafx.h"
#include "net/Socket.hpp"
#include "net/StreamSocketImpl.hpp"
#include <algorithm>
#include <string.h>

NET_NAMESPACE_BEGIN

CSocket::CSocket() : m_pImpl(new CStreamSocketImpl)
{
}

CSocket::CSocket(CSocketImpl* pImpl) : m_pImpl(pImpl)
{
	jh_check_ptr (m_pImpl);
}

CSocket::CSocket(const CSocket& socket) : m_pImpl(socket.m_pImpl)
{
	jh_check_ptr (m_pImpl);

	m_pImpl->add();
}

CSocket& CSocket::operator = (const CSocket& socket)
{
	if (&socket != this)
	{
		if (m_pImpl) m_pImpl->release();
		m_pImpl = socket.m_pImpl;
		if (m_pImpl) m_pImpl->add();
	}
	return *this;
}

CSocket::~CSocket()
{
	m_pImpl->release();
}

bool CSocket::operator == (const CSocket& socket) const
{
	return m_pImpl == socket.m_pImpl;
}

bool CSocket::operator != (const CSocket& socket) const
{
	return m_pImpl != socket.m_pImpl;
}

bool CSocket::operator <  (const CSocket& socket) const
{
	return m_pImpl < socket.m_pImpl;
}

bool CSocket::operator <= (const CSocket& socket) const
{
	return m_pImpl <= socket.m_pImpl;
}

bool CSocket::operator >  (const CSocket& socket) const
{
	return m_pImpl > socket.m_pImpl;
}

bool CSocket::operator >= (const CSocket& socket) const
{
	return m_pImpl >= socket.m_pImpl;
}

void CSocket::close()
{
	m_pImpl->close();
}

int CSocket::select(SocketList& readList, SocketList& writeList, SocketList& exceptList, const CTimeSpan& timeout)
{
	fd_set fdRead;
	fd_set fdWrite;
	fd_set fdExcept;
	int nfd = 0;
	FD_ZERO(&fdRead);
	for (SocketList::const_iterator it = readList.begin(); it != readList.end(); ++it)
	{
		JH_SOCKET fd = it->sockfd();
		if (fd != JH_INVALID_SOCKET)
		{
			if (int(fd) > nfd)
				nfd = int(fd);
			FD_SET(fd, &fdRead);
		}
	}
	FD_ZERO(&fdWrite);
	for (SocketList::const_iterator it = writeList.begin(); it != writeList.end(); ++it)
	{
		JH_SOCKET fd = it->sockfd();
		if (fd != JH_INVALID_SOCKET)
		{
			if (int(fd) > nfd)
				nfd = int(fd);
			FD_SET(fd, &fdWrite);
		}
	}
	FD_ZERO(&fdExcept);
	for (SocketList::const_iterator it = exceptList.begin(); it != exceptList.end(); ++it)
	{
		JH_SOCKET fd = it->sockfd();
		if (fd != JH_INVALID_SOCKET)
		{
			if (int(fd) > nfd)
				nfd = int(fd);
			FD_SET(fd, &fdExcept);
		}
	}

	if (nfd == 0)
		return 0;

	CTimeSpan remainingTime(timeout);
	int rc;
	do
	{
		struct timeval tv;
		tv.tv_sec  = (long) remainingTime.totalSeconds();
		tv.tv_usec = (long) remainingTime.useconds();
		CTimeStamp start;
		rc = ::select(nfd + 1, &fdRead, &fdWrite, &fdExcept, &tv);
		if (rc < 0 && CSocketImpl::lastError() == JH_EINTR)
		{
			CTimeStamp end;
			CTimeSpan waited = end - start;
			if (waited < remainingTime)
				remainingTime -= waited;
			else
				remainingTime = 0;
		}
	}while (rc < 0 && CSocketImpl::lastError() == JH_EINTR);

	if (rc < 0)
		CSocketImpl::error();

	SocketList readyReadList;
	for (SocketList::const_iterator it = readList.begin(); it != readList.end(); ++it)
	{
		JH_SOCKET fd = it->sockfd();
		if (fd != JH_INVALID_SOCKET)
		{
			if (FD_ISSET(fd, &fdRead))
				readyReadList.push_back(*it);
		}
	}
	std::swap(readList, readyReadList);
	SocketList readyWriteList;
	for (SocketList::const_iterator it = writeList.begin(); it != writeList.end(); ++it)
	{
		JH_SOCKET fd = it->sockfd();
		if (fd != JH_INVALID_SOCKET)
		{
			if (FD_ISSET(fd, &fdWrite))
				readyWriteList.push_back(*it);
		}
	}
	std::swap(writeList, readyWriteList);
	SocketList readyExceptList;
	for (SocketList::const_iterator it = exceptList.begin(); it != exceptList.end(); ++it)
	{
		JH_SOCKET fd = it->sockfd();
		if (fd != JH_INVALID_SOCKET)
		{
			if (FD_ISSET(fd, &fdExcept))
				readyExceptList.push_back(*it);
		}
	}
	std::swap(exceptList, readyExceptList);	
	return rc; 
}


bool CSocket::poll(const CTimeSpan& timeout, int mode) const
{
	return m_pImpl->poll(timeout, mode);
}

int CSocket::available() const
{
	return m_pImpl->available();
}

void CSocket::setSendBufferSize(int size)
{
	m_pImpl->setSendBufferSize(size);
}

int CSocket::getSendBufferSize() const
{
	return m_pImpl->getSendBufferSize();
}

void CSocket::setReceiveBufferSize(int size)
{
	m_pImpl->setReceiveBufferSize(size);
}

int CSocket::getReceiveBufferSize() const
{
	return m_pImpl->getReceiveBufferSize();
}

void CSocket::setSendTimeout(const CTimeSpan& timeout)
{
	m_pImpl->setSendTimeout(timeout);
}

CTimeSpan CSocket::getSendTimeout() const
{
	return m_pImpl->getSendTimeout();
}

void CSocket::setReceiveTimeout(const CTimeSpan& timeout)
{
	m_pImpl->setReceiveTimeout(timeout);
}

CTimeSpan CSocket::getReceiveTimeout() const
{
	return m_pImpl->getReceiveTimeout();
}

void CSocket::setOption(int level, int option, int value)
{
	m_pImpl->setOption(level, option, value);
}

void CSocket::setOption(int level, int option, unsigned value)
{
	m_pImpl->setOption(level, option, value);
}

void CSocket::setOption(int level, int option, unsigned char value)
{
	m_pImpl->setOption(level, option, value);
}

void CSocket::setOption(int level, int option, const CTimeSpan& value)
{
	m_pImpl->setOption(level, option, value);
}

void CSocket::setOption(int level, int option, const CIPAddress& value)
{
	m_pImpl->setOption(level, option, value);
}

void CSocket::getOption(int level, int option, int& value) const
{
	m_pImpl->getOption(level, option, value);
}

void CSocket::getOption(int level, int option, unsigned& value) const
{
	m_pImpl->getOption(level, option, value);
}

void CSocket::getOption(int level, int option, unsigned char& value) const
{
	m_pImpl->getOption(level, option, value);
}

void CSocket::getOption(int level, int option, CTimeSpan& value) const
{
	m_pImpl->getOption(level, option, value);
}

void CSocket::getOption(int level, int option, CIPAddress& value) const
{
	m_pImpl->getOption(level, option, value);
}

void CSocket::setLinger(bool on, int seconds)
{
	m_pImpl->setLinger(on, seconds);
}

void CSocket::getLinger(bool& on, int& seconds) const
{
	m_pImpl->getLinger(on, seconds);
}

void CSocket::setNoDelay(bool flag)
{
	m_pImpl->setNoDelay(flag);
}

bool CSocket::getNoDelay() const
{
	return m_pImpl->getNoDelay();
}

void CSocket::setKeepAlive(bool flag)
{
	m_pImpl->setKeepAlive(flag);
}

bool CSocket::getKeepAlive() const
{
	return m_pImpl->getKeepAlive();
}

void CSocket::setReuseAddress(bool flag)
{
	m_pImpl->setReuseAddress(flag);
}

bool CSocket::getReuseAddress() const
{
	return m_pImpl->getReuseAddress();
}

void CSocket::setReusePort(bool flag)
{
	m_pImpl->setReusePort(flag);
}

bool CSocket::getReusePort() const
{
	return m_pImpl->getReusePort();
}

void CSocket::setOOBInline(bool flag)
{
	m_pImpl->setOOBInline(flag);
}

bool CSocket::getOOBInline() const
{
	return m_pImpl->getOOBInline();
}

void CSocket::setBlocking(bool flag)
{
	m_pImpl->setBlocking(flag);
}

bool CSocket::getBlocking() const
{
	return m_pImpl->getBlocking();
}

CSocketImpl* CSocket::impl() const
{
	return m_pImpl;
}

JH_SOCKET CSocket::sockfd() const
{
	return m_pImpl->sockfd();
}

CSocketAddress CSocket::address() const
{
	return m_pImpl->address();
}

CSocketAddress CSocket::peerAddress() const
{
	return m_pImpl->peerAddress();
}

bool CSocket::secure() const
{
	return m_pImpl->secure();
}

bool CSocket::supportsIPv4()
{
	return true;
}

bool CSocket::supportsIPv6()
{
#if defined(JH_HAVE_IPv6)
	return true;
#else
	return false;
#endif
}

void CSocket::init(int af)
{
	m_pImpl->init(af);
}

NET_NAMESPACE_END
